#include <stdio.h>
#include <stdlib.h>
#include "single_list.h"


#define destroy_node(node, destroy) \
	{\
    		if (NULL != node->data)\
    		{\
			destroy(node->data);\
    		}\
    		free(node);\
		}

struct single_node
{
    struct single_node *next;
    void *data;
};

struct single_list
{
    struct single_node *first;
    SListCmpFunc cmp;	/*We use these to functions to make the list more useful in different environment.*/
    int count;
};

static _SNode* create_node(void *data)
{
    if (NULL == data)
    {
	return NULL;
    }

    _SNode *new_node = NULL;
    new_node = (_SNode *)malloc(sizeof(_SNode));
    if (NULL == new_node)
    {
	return NULL;
    }
    new_node->next = NULL;
    new_node->data = NULL;

    return new_node;
}

_SList create_list(SListCmpFunc cmp)
{
    _SList new_list = NULL;
    if (NULL == cmp)
    {
	return NULL;
    }

    new_list = (_SList)malloc(sizeof(*new_list));
    if (NULL == new_list)
    {
	return NULL;
    }

    new_list->first = NULL;
    new_list->cmp = cmp;
    new_list->count = 0;

    return new_list;
}

_SListRet destroy_list(_SList thiz, SListNodeFunc destroy)
{
    _SNode *del = NULL;
    if (thiz == NULL || destroy == NULL)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

    for (del = thiz->first; del != NULL; del = thiz->first)
    {
	thiz->first = del->next;
	destroy_node(del, destroy);
    }

    free(thiz);
    return _SLIST_RET_OK_;
}

_SListRet slist_append(_SList thiz, void *data)	/*With this function we may not need the create_node
						  and insert_to_list function anymore.*/
{
    if (NULL == thiz || NULL == data)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

    _SNode *new_node = NULL;
    new_node = (_SNode *)malloc(sizeof(_SNode));
    if (NULL == new_node)
    {
	return _SLIST_RET_CTRL_FAIL_;
    }

    new_node->data = data;

    (thiz->count)++;
    new_node->next = thiz->first;
    thiz->first = new_node;

    return _SLIST_RET_OK_;
}

_SListRet slist_foreach(_SList thiz, SListVisitFunc visit, void *ctx)
{
    _SNode *iter = NULL;

    if (NULL == thiz || visit == NULL)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

    for (iter = thiz->first; NULL != iter; iter = iter->next)
    {
	if (visit(ctx, iter->data) != _SLIST_RET_OK_)
	{
	    return _SLIST_RET_CTRL_FAIL_;
	}
    }

    return _SLIST_RET_OK_;
}

_SListRet is_exist(_SList thiz, void *key)
{
    _SNode *iter = NULL;
    _SListRet ret = _SLIST_RET_OK_;

    if (NULL == thiz || NULL == key)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

    for(iter = thiz->first; NULL != iter; iter = iter->next)
    {
	ret = thiz->cmp(key, iter->data);
	if (ret == _SLIST_EQUEAL_)
	{
	    return _SLIST_NODE_EXIST_;
	}
    }
	
    return _SLIST_NODE_NEXIST_;
}

static _SListRet insert_to_list(_SList thiz, _SNode *node)
{
    if (NULL == thiz || NULL == node)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

#if 0	/*My be we need to leave this part to user */
    if (is_exist(thiz, cmp, node->data) == _SLIST_NODE_EXIST_)
    {
	return _SLIST_RET_CTRL_FAIL_;
    }
#endif

    (thiz->count)++;
    node->next = thiz->first;
    thiz->first = node;

    return _SLIST_RET_OK_;
}

_SListRet delete_node(_SList thiz, SListNodeFunc destroy, void *key)
{
    _SNode *del = NULL;
    _SNode *iter = NULL;

    if (NULL == thiz || NULL == key || NULL == destroy)
    {
	return _SLIST_RET_INVALID_PARAMS_;
    }

    while (thiz->first != NULL)
    {
	if (_SLIST_EQUEAL_ == thiz->cmp(key, thiz->first->data))
	{
	    del = thiz->first;
	    thiz->first = thiz->first->next;
	    destroy_node(del, destroy);
	    (thiz->count)--;
	    continue;
	}

	break;
    }
    
    if (thiz->first == NULL)
    {
	return _SLIST_RET_OK_;
    }

    iter = thiz->first;
    while(iter->next != NULL)
    {
	if (_SLIST_EQUEAL_ == thiz->cmp(key, iter->next->data))
	{
	    del = iter->next;
	    iter->next = iter->next->next;
	    destroy_node(del, destroy);
	    del = NULL;
	    (thiz->count)--;
	    continue;
	}
	iter = iter->next;
    }

    return _SLIST_RET_OK_;
}

